
#ifndef RTTIMEVALUE_H
#define RTTIMEVALUE_H

#include "RtDefines.h"

#define RT_ONE_SECOND_IN_MSECS 1000L
#define RT_ONE_SECOND_IN_USECS 1000000L
#define RT_ONE_SECOND_IN_NSECS 1000000000L

#ifdef _MSC_VER
  // -------------------------------------------------------------------
  // These forward declarations are only used to circumvent a bug in
  // MSVC 6.0 compiler.  They shouldn't cause any problem for other
  // compilers.
  class CRtTimeValue;
  RT_FW_EXPORT CRtTimeValue operator + (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT CRtTimeValue operator - (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator < (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator > (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator <= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator >= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator == (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
  RT_FW_EXPORT int operator != (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
#endif // _MSC_VER

class RT_FW_EXPORT CRtTimeValue  
{
public:
	// add the follwoing two functions to avoid call Normalize().
	CRtTimeValue();
	CRtTimeValue(time_t aSec);
	CRtTimeValue(time_t aSec, LONG aUsec);
	CRtTimeValue(const timeval &aTv);
	CRtTimeValue(double aSec);
	
	void Set(time_t aSec, LONG aUsec);
	void Set(const timeval &aTv);
	void Set(double aSec);

	time_t GetSec() const ;
	LONG GetUsec() const ;

	void SetByTotalMsec(LONG aMilliseconds);
	LONGLONG GetTotalInMsec() const;
	LONGLONG GetTotalInUsec() const;

	void operator += (const CRtTimeValue &aRight);
	void operator -= (const CRtTimeValue &aRight);

	friend RT_FW_EXPORT CRtTimeValue operator + (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT CRtTimeValue operator - (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator < (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator > (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator <= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator >= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator == (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);
	friend RT_FW_EXPORT int operator != (const CRtTimeValue &aLeft, const CRtTimeValue &aRight);

	static CRtTimeValue GetTimeOfDay();
	static const CRtTimeValue s_tvZero;
	static const CRtTimeValue s_tvMax;
	
private:
	void Normalize();
	
	time_t m_lSec;
	LONG m_lUsec;
};


// inline functions
inline CRtTimeValue::CRtTimeValue()
	: m_lSec(0)
	, m_lUsec(0)
{
}

inline CRtTimeValue::CRtTimeValue(time_t aSec)
	: m_lSec(aSec)
	, m_lUsec(0)
{
}

inline CRtTimeValue::CRtTimeValue(time_t aSec, LONG aUsec)
{
	Set(aSec, aUsec);
}

inline CRtTimeValue::CRtTimeValue(const timeval &aTv)
{
	Set(aTv);
}

inline CRtTimeValue::CRtTimeValue(double aSec)
{
	Set(aSec);
}

inline void CRtTimeValue::Set(time_t aSec, LONG aUsec)
{
	m_lSec = aSec;
	m_lUsec = aUsec;
	Normalize();
}

inline void CRtTimeValue::Set(const timeval &aTv)
{
	m_lSec = aTv.tv_sec;
	m_lUsec = aTv.tv_usec;
	Normalize();
}

inline void CRtTimeValue::Set(double aSec)
{
	LONG l = (LONG)aSec;
	m_lSec = l;
	m_lUsec = (LONG)((aSec - (double)l) * RT_ONE_SECOND_IN_USECS);
	Normalize();
}

inline void CRtTimeValue::SetByTotalMsec(LONG aMilliseconds)
{
	m_lSec = aMilliseconds / 1000;
	m_lUsec = (LONG)(aMilliseconds - (m_lSec * 1000)) * 1000;
}

inline time_t CRtTimeValue::GetSec() const 
{
	return m_lSec;
}

inline LONG CRtTimeValue::GetUsec() const 
{
	return m_lUsec;
}

inline LONGLONG CRtTimeValue::GetTotalInMsec() const
{
	return (LONGLONG)m_lSec * 1000 + m_lUsec / 1000;
}

inline LONGLONG CRtTimeValue::GetTotalInUsec() const
{
	return (LONGLONG)m_lSec * 1000000 + m_lUsec;
}

#ifndef RT_WIN32
inline CRtTimeValue CRtTimeValue::GetTimeOfDay()
{
	timeval tvCur;
	::gettimeofday(&tvCur, NULL);

	/// Fix linux kernel bug on gettimeofday() jumping into the future. 
	/// https://bugzilla.redhat.com/show_bug.cgi?id=452185
	static timeval s_lastTick = {0, 0};
	if (tvCur.tv_sec>s_lastTick.tv_sec+3600 && s_lastTick.tv_sec!=0)
	{
		::gettimeofday(&tvCur, NULL);
	}
	s_lastTick = tvCur;
	
	return CRtTimeValue(tvCur);
}
#endif // !RT_WIN32

inline int operator > (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	if (aLeft.GetSec() > aRight.GetSec())
		return 1;
	else if (aLeft.GetSec() == aRight.GetSec() && aLeft.GetUsec() > aRight.GetUsec())
		return 1;
	else
		return 0;
}

inline int operator >= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	if (aLeft.GetSec() > aRight.GetSec())
		return 1;
	else if (aLeft.GetSec() == aRight.GetSec() && aLeft.GetUsec() >= aRight.GetUsec())
		return 1;
	else
		return 0;
}

inline int operator < (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return aRight > aLeft;
}

inline int operator <= (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return aRight >= aLeft;
}

inline int operator == (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return aLeft.GetSec() == aRight.GetSec() && 
		   aLeft.GetUsec() == aRight.GetUsec();
}

inline int operator != (const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return !(aLeft == aRight);
}

inline void CRtTimeValue::operator += (const CRtTimeValue &aRight)
{
	m_lSec = GetSec() + aRight.GetSec();
	m_lUsec = GetUsec() + aRight.GetUsec();
	Normalize();
}

inline void CRtTimeValue::operator -= (const CRtTimeValue &aRight)
{
	m_lSec = GetSec() - aRight.GetSec();
	m_lUsec = GetUsec() - aRight.GetUsec();
	Normalize();
}

inline CRtTimeValue operator + 
(const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return CRtTimeValue(aLeft.GetSec() + aRight.GetSec(), 
					  aLeft.GetUsec() + aRight.GetUsec());
}

inline CRtTimeValue operator - 
(const CRtTimeValue &aLeft, const CRtTimeValue &aRight)
{
	return CRtTimeValue(aLeft.GetSec() - aRight.GetSec(), 
					  aLeft.GetUsec() - aRight.GetUsec());
}

#endif // !RTTIMEVALUE_H
